<script setup>
import * as dat from 'lil-gui'
import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader.js'
import firefliesVertexShader from './shaders/fireflies/vertex.glsl?raw'
import firefliesFragmentShader from './shaders/fireflies/fragment.glsl?raw'
import portalVertexShader from './shaders/portal/vertex.glsl?raw'
import portalFragmentShader from './shaders/portal/fragment.glsl?raw'

/**
 * Base
 */
// Debug
const debugObject = {}
const gui = new dat.GUI({
  width: 400
})

// Scene
const scene = new THREE.Scene()

/**
 * Loaders
 */
// Texture loader
const textureLoader = new THREE.TextureLoader()

// Draco loader
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('./draco/')

// GLTF loader
const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)

/**
 * Textures
 */
// const bakedTexture = textureLoader.load('baked.jpg')
const bakedTexture = textureLoader.load('./texture.jpg')
bakedTexture.flipY = false

/**
 * Materials
 */
const bakedMaterial = new THREE.MeshBasicMaterial({map: bakedTexture})

// Pole light material
const poleLightMaterial = new THREE.MeshBasicMaterial({color: 0xffffe5})

// Portal light material
debugObject.portalColorStart = '#ff0000'
debugObject.portalColorEnd = '#0000ff'

gui
    .addColor(debugObject, 'portalColorStart')
    .onChange(() => {
      portalLightMaterial.uniforms.uColorStart.value.set(debugObject.portalColorStart)
    })

gui
    .addColor(debugObject, 'portalColorEnd')
    .onChange(() => {
      portalLightMaterial.uniforms.uColorEnd.value.set(debugObject.portalColorEnd)
    })

const portalLightMaterial = new THREE.ShaderMaterial({
  uniforms: {
    uTime: {value: 0},
    uColorStart: {value: new THREE.Color(debugObject.portalColorStart)},
    uColorEnd: {value: new THREE.Color(debugObject.portalColorEnd)}
  }, vertexShader: portalVertexShader, fragmentShader: portalFragmentShader
})

/**
 * Model
 */
gltfLoader.load('./portal1.glb', (gltf) => {
  scene.add(gltf.scene)
  // Get each object
  const bakedMesh = gltf.scene.children.find((child) => child.name === 'baked')
  const portalLightMesh = gltf.scene.children.find((child) => child.name === 'portalLight')
  const poleLightAMesh = gltf.scene.children.find((child) => child.name === 'poleLightA')
  const poleLightBMesh = gltf.scene.children.find((child) => child.name === 'poleLightB')

  // Apply materials
  bakedMesh.material = bakedMaterial
  portalLightMesh.material = portalLightMaterial
  poleLightAMesh.material = poleLightMaterial
  poleLightBMesh.material = poleLightMaterial
}, (xhr) => {
  console.log(xhr)
  console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
})

/**
 * Fireflies
 */
// Geometry
const firefliesGeometry = new THREE.BufferGeometry()
const firefliesCount = 330
const positionArray = new Float32Array(firefliesCount * 3)
const scaleArray = new Float32Array(firefliesCount)

for (let i = 0; i < firefliesCount; i++) {
  positionArray[i * 3 + 0] = (Math.random() - 0.5) * 20
  positionArray[i * 3 + 1] = Math.random() * 1.5 * 4
  positionArray[i * 3 + 2] = (Math.random() - 0.5) * 20

  scaleArray[i] = Math.random()
}

firefliesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))
firefliesGeometry.setAttribute('aScale', new THREE.BufferAttribute(scaleArray, 1))

// Material
const firefliesMaterial = new THREE.ShaderMaterial({
  uniforms: {
    uTime: {value: 0}, uPixelRatio: {value: Math.min(window.devicePixelRatio, 2)}, uSize: {value: 350}
  },
  vertexShader: firefliesVertexShader,
  fragmentShader: firefliesFragmentShader,
  transparent: true,
  blending: THREE.AdditiveBlending,
  depthWrite: false
})

gui.add(firefliesMaterial.uniforms.uSize, 'value').min(0).max(500).step(1).name('firefliesSize')

// Points
const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial)
scene.add(fireflies)

/**
 * Sizes
 */
const sizes = {
  width: window.innerWidth, height: window.innerHeight
}

window.addEventListener('resize', () => {
  // Update sizes
  sizes.width = window.innerWidth
  sizes.height = window.innerHeight

  // Update camera
  camera.aspect = sizes.width / sizes.height
  camera.updateProjectionMatrix()

  // Update renderer
  renderer.setSize(sizes.width, sizes.height)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

  // Update fireflies
  firefliesMaterial.uniforms.uPixelRatio.value = Math.min(window.devicePixelRatio, 2)
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(45, sizes.width / sizes.height, 0.1, 100)
camera.position.x = 0
camera.position.y = 3
camera.position.z = 14
scene.add(camera)


/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({antialias: true})
// renderer.outputEncoding = THREE.sRGBEncoding
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
document.body.appendChild(renderer.domElement);

debugObject.clearColor = '#201919'
renderer.setClearColor(debugObject.clearColor)

// Controls
const controls = new OrbitControls(camera, renderer.domElement)

controls.enableDamping = true

gui
    .addColor(debugObject, 'clearColor')
    .onChange(() => {
      renderer.setClearColor(debugObject.clearColor)
    })

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () => {
  const elapsedTime = clock.getElapsedTime()

  // Update materials
  portalLightMaterial.uniforms.uTime.value = elapsedTime
  firefliesMaterial.uniforms.uTime.value = elapsedTime

  // Update controls
  controls.update()

  // Render
  renderer.render(scene, camera)

  // Call tick again on the next frame
  window.requestAnimationFrame(tick)
}

tick()
</script>

<template>
</template>

<style scoped>
* {
  margin: 0;
  padding: 0;
}
::-webkit-scrollbar {
  width: 0;
}
body {
  width: 100%;
  height: 100%;
}
canvas {
  display: block;
  position: fixed;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
}
</style>
